package com.pj.config;

import com.pj.DBRouterConfig;
import com.pj.DBRouterJoinPoint;
import com.pj.dynamic.DynamicDataSource;
import com.pj.dynamic.DynamicMybatisPlugin;
import com.pj.strategy.DBRouterStrategyHashCode;
import com.pj.strategy.IDBRouterStrategy;
import com.pj.util.PropertyUtil;
import org.apache.ibatis.plugin.Interceptor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;

import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @program: springboot-pure
 * @ClassName DataSourceAutoConfig
 * @description:
 * @author: gzf
 * @create: 2023-11-16 17:03
 * @Version 2.0
 **/
@Configuration
public class DataSourceAutoConfig implements EnvironmentAware {

    private Integer dbCount;

    private Integer tbCount;

    private String routerKey;

    /**
     * 数据源配置组
     */
    private final Map<String, Map<String, Object>> dataSourceMap = new HashMap<>();

    /**
     * 默认数据源配置
     */
    private Map<String, Object> defaultDataSourceConfig;




    @Bean(name = "db-router-point")
    @ConditionalOnMissingBean
    public DBRouterJoinPoint point(DBRouterConfig dbRouterConfig, IDBRouterStrategy dbRouterStrategy) {
        return new DBRouterJoinPoint(dbRouterConfig, dbRouterStrategy);
    }

    @Bean
    public IDBRouterStrategy dbRouterStrategy(DBRouterConfig dbRouterConfig) {
        return new DBRouterStrategyHashCode(dbRouterConfig);
    }


    @Bean
    public DBRouterConfig dbRouterConfig() {
        return new DBRouterConfig(dbCount, tbCount, routerKey);
    }

    @Bean
    public Interceptor plugin() {
        return new DynamicMybatisPlugin();
    }


    @Bean
    public DataSource createDataSource() {
        HashMap<Object, Object> targetDataSources = new HashMap<>();
        dataSourceMap.keySet().forEach(dbInfo -> {
            Map<String, Object> propertyMap = this.dataSourceMap.get(dbInfo);
            DataSource dataSource = createDataSource(propertyMap);
            targetDataSources.put(dbInfo, dataSource);
        });

        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(createDataSource(defaultDataSourceConfig));
        return dynamicDataSource;
    }

    @Bean
    public TransactionTemplate transactionTemplate(DataSource dataSource) {
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);

        TransactionTemplate transactionTemplate = new TransactionTemplate();
        transactionTemplate.setTransactionManager(dataSourceTransactionManager);
        transactionTemplate.setPropagationBehaviorName("PROPAGATION_REQUIRED");
        return transactionTemplate;
    }

    private DataSource createDataSource(Map<String, Object> attributes) {
        try {
            DataSourceProperties dataSourceProperties = new DataSourceProperties();
            dataSourceProperties.setUrl((String) attributes.get("url"));
            dataSourceProperties.setUsername((String) attributes.get("username"));
            dataSourceProperties.setPassword((String) attributes.get("password"));

            String driverClassName = attributes.get("driver-class-name") == null ? "com.zaxxer.hikari.HikariDataSource" : attributes.get("driver-class-name").toString();
            dataSourceProperties.setDriverClassName(driverClassName);

            String typeClassName = attributes.get("type-class-name") == null ? "com.zaxxer.hikari.HikariDataSource" : attributes.get("type-class-name").toString();

            return dataSourceProperties
                    .initializeDataSourceBuilder()
                    .type((Class<DataSource>) Class.forName(typeClassName))
                    .build();
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("can not find datasource type class by class name", e);
        }
    }

    @Override
    public void setEnvironment(Environment environment) {
        String prefix = "mini-db-router.jdbc.datasource.";
        dbCount = environment.getProperty(prefix + "dbCount", Integer.class);
        tbCount = environment.getProperty(prefix + "tbCount", Integer.class);
        //全局配置的key
        routerKey = environment.getProperty(prefix + "routerKey");
        // 分库分表数据源
        String dataSources = environment.getProperty(prefix + "list");
        assert dataSources != null;
        Arrays.stream(dataSources.split(",")).forEach(dbInfo -> {
            String dbPrefix = prefix + dbInfo;
            Map<String, Object> dataSource = PropertyUtil.handle(dbPrefix, environment);
            dataSourceMap.put(dbInfo, dataSource);
        });
        //默认数据源
        String defaultData = environment.getProperty(prefix + "default");
        defaultDataSourceConfig = PropertyUtil.handle(prefix + defaultData, environment);

    }
}
